package me.flyray.bsin.oss.ipfs;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import me.flyray.bsin.enums.TenantAppType;
import org.apache.http.entity.ContentType;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Service;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import io.ipfs.api.Multipart;
import io.ipfs.api.NamedStreamable;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;

@Service
@Slf4j
public class BsinIpfsService {

    @Value("${bsin.s11edao.ipfs.api}")
    private String ipfsApi;
    @Value("${bsin.s11edao.ipfs.gateway}")
    private String ipfsGateway;
    @Value("${message.upPath}")
    private String upPath;
    @Value("${message.preImgUrl}")
    private String preImgUrl;

    /**
     * 将文件上传到ipfs服务节点上
     *
     * @param bytes
     * @param fileName
     * @return
     * @throws IOException
     */
    public JSONObject ipfsAdd(byte[] bytes, String fileName) throws IOException {

        String apiName = "/add";
        String url = ipfsApi + apiName;
        String suffixName = fileName.substring(fileName.lastIndexOf("."));
        String newUUID = UUID.randomUUID().toString().replaceAll("-", "");
        String newFileName = newUUID + suffixName;
        Multipart m = new Multipart(url, "UTF-8");
        NamedStreamable.ByteArrayWrapper filew = new NamedStreamable.ByteArrayWrapper(newFileName, bytes);
        m.addFilePart(fileName, Paths.get(""), filew);
        String res = m.finish();
        JSONObject result = JSONObject.parseObject(res);
        String Hash = result.get("Hash").toString();
        result.put("fileUrl", ipfsGateway + Hash);
        result.put("fileHash", Hash);
        return result;
    }

    public String mkDir(String dirpath) throws IOException {
        //http://ipfsadmin.daxiongmao.art/api/v0/files/mkdir?parents=true&arg=%2F1
        String apiName = "/files/mkdir";
        String argument = "arg=" + dirpath + "&parents=true";
        String url = ipfsApi + apiName + "?" + argument;
        System.out.println(url);
        Multipart cpm = new Multipart(url, "UTF-8");
        return cpm.finish();
    }

    public JSONObject fileStat(String currentPath) throws IOException {

        String apiName = "/files/stat";
        String argument = "arg=" + currentPath;
        String url = ipfsApi + apiName + "?" + argument;
        //System.out.println(url);
        Multipart cpm = new Multipart(url, "UTF-8");
        try {
            String req = cpm.finish();
            return JSONObject.parseObject(req);
        } catch (RuntimeException e) {
            this.mkDir(currentPath);
            String req = new Multipart(url, "UTF-8").finish();
            return JSONObject.parseObject(req);
        }
    }

    public JSONObject fileLS(String hashDir) throws IOException {
        String apiName = "/ls";
        String argument = "arg=" + hashDir;
        String url = ipfsApi + apiName + "?" + argument;
        //System.out.println(url);
        Multipart cpm = new Multipart(url, "UTF-8");
        String req = cpm.finish();
        JSONObject result = JSONObject.parseObject(req);
        Object objs = result.getJSONArray("Objects").get(0);
        if (objs instanceof JSONObject) {
            JSONArray links = ((JSONObject) objs).getJSONArray("Links");
            for (Object e : links) {
                if (e instanceof JSONObject) {
                    ((JSONObject) e).put("Url", ipfsGateway + ((Map) e).get("Hash").toString());
                }
            }
        }
        return result;
    }

    public void ipfdCP(String hash, String currentPath, String filename) throws IOException {
        String cpTarget = currentPath + "/" + filename;
        String apiName = "/files/cp";
        String arguments = "arg=%s&arg=%s";
        String fileHash = "/ipfs/" + hash;
        String argument = String.format(arguments, fileHash, cpTarget);
        String url = ipfsApi + apiName + "?" + argument;
        //System.out.println(url);
        Multipart cpm = new Multipart(url, "UTF-8");
        cpm.finish();
    }

    public JSONObject ipfsAndServiceUpload(MultipartFile file, String tenantId, String currentPath, String tenantAppType) throws IOException {
        if (file.isEmpty()) {
            throw new IllegalArgumentException("File is empty");
        }
        String fileName = file.getOriginalFilename();
        if (fileName == null) {
            return null;
        }
        // 根据用户类型判断用户属于哪个平台
        String dev = "bigan";
        if (TenantAppType.BSIN_JIUJIU.getCode().equals(tenantAppType)) {
            dev = "jiujiu";
        } else if (TenantAppType.BSIN_DAOBOOK.getCode().equals(tenantAppType)) {
            dev = "daobook";
        }
        String localPath = upPath + dev + "/" + tenantId + currentPath + "/";
        currentPath = "/" + dev + "/" + tenantId + currentPath;
        JSONObject result = null;
        byte[] fileByte = null;
        // 上传文件到服务器
        try {
            Path path = Paths.get(localPath);
            Path pathCreate = Files.createDirectories(path);
            System.out.println("上传的文件名为：" + fileName);
            // 获取文件的后缀名
            String suffixName = fileName.substring(fileName.lastIndexOf("."));
            System.out.println("上传的后缀名为：" + suffixName);
            File dest = new File(localPath + fileName);
            // 检测是否存在目录
            if (!dest.getParentFile().exists()) {
                dest.getParentFile().mkdirs();
            }
            String url = preImgUrl + fileName;
            Map<String, Object> resMap = new HashMap<String, Object>();
            fileByte = file.getBytes();
            try {
                file.transferTo(dest);
                resMap.put("oldFileName", fileName);
                resMap.put("url", url);
            } catch (IllegalStateException e) {
                throw new IllegalArgumentException(e.toString());
            }
        } catch (Exception e) {
            System.out.println("存储本地服务器 error...." + e.toString());
            throw new IllegalArgumentException(e.toString());
        }
        // 上传文件到ipfs----会有 java.lang.RuntimeException: ipfsadmin.s11edao.com 异常，忽略
        result = ipfsAdd(fileByte, fileName);
        try {
            //检查当前文件夹是否存在-不存在则创建
            String hashDir = fileStat(currentPath).get("Hash").toString();
            log.info("fileStat: ", hashDir);
            // 移植到一个目录文件夹
            ipfdCP(result.get("Hash").toString(), currentPath, fileName);
        } catch (Exception e) {
            //TODO 目录移植失败不影响使用，目录下文件存在则会抛出异常
            System.out.println("ipfdCP error...." + e.toString());
        }
        return result;
    }

    public MultipartFile string2multipartFile(String content, String fileName) throws IOException {
        MultipartFile multipartFile = null;
        String tmpPath = "./";
        File tmpFile = null;
        // 创建文件目录
        File dir = new File(tmpPath);
        if (!dir.exists() && !dir.isDirectory()) {
            dir.mkdirs();
        }

        BufferedOutputStream bos = null;
        java.io.FileOutputStream fos = null;
        try {
            byte[] bytes = content.getBytes();
            tmpFile = new File(tmpPath + fileName);
            fos = new java.io.FileOutputStream(tmpFile);
            bos = new BufferedOutputStream(fos);
            bos.write(bytes);
        } catch (
                Exception e) {
            log.error("string to bytes failed: ", e);
        } finally {
            if (bos != null) {
                try {
                    bos.close();
                    FileInputStream fileInputStream = new FileInputStream(tmpFile);
                    // contentType根据需求调整
                    multipartFile = new MockMultipartFile(tmpFile.getName(), tmpFile.getName(), ContentType.TEXT_PLAIN.toString(), fileInputStream);
                    tmpFile.delete();
                } catch (IOException e) {
                    log.error("bytesToFile close bos failed: ", e);
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    log.error("bytesToFile close fos failed: ", e);
                }
            }
        }
        return multipartFile;
    }

    public String getIpfsCid(String tokenURI) {
        String tmpStr = tokenURI.substring(0, tokenURI.indexOf("/ipfs/"));
        log.info(tmpStr);
        String metadataCID = tokenURI.substring(tmpStr.length() + 6, tokenURI.length());
        log.info(metadataCID);
        return metadataCID;
    }
}
